package com.xbongbong.pro.statistic.help;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.xbongbong.paas.bi.pojo.DataSetNodeFieldPojo;
import com.xbongbong.paas.bi.pojo.DataSetNodeGroupPojo;
import com.xbongbong.paas.bi.pojo.DataSetNodeInputPojo;
import com.xbongbong.paas.bi.pojo.DataSetNodeJoinPojo;
import com.xbongbong.paas.bi.pojo.DataSetNodeOutputPojo;
import com.xbongbong.paas.bi.pojo.DataSetNodePojo;
import com.xbongbong.paas.bi.pojo.DataSetNodeUnionPojo;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.XbbConstant;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.bi.ChartSourceEnum;
import com.xbongbong.paas.enums.bi.DataSetFieldStatusEnum;
import com.xbongbong.paas.enums.bi.DataSetJoinEnum;
import com.xbongbong.paas.enums.bi.DataSetNodeTypeEnum;
import com.xbongbong.paas.enums.bi.DataSetOperateEnum;
import com.xbongbong.paas.enums.bi.DataSetPermissionEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.VisibleRulePoJo;
import com.xbongbong.paas.field.VisibleRuleVO;
import com.xbongbong.sys.help.PackageHelp;
import com.xbongbong.paas.pojo.BiExplainPojo;
import com.xbongbong.paas.pojo.DsAttrValuePojo;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.wrap.RoleSimpleVO;
import com.xbongbong.paas.toolbox.wrap.UserVO;
import com.xbongbong.pro.config.ProBaseConfig;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.domain.entity.DataSetEntity;
import com.xbongbong.pro.domain.entity.ext.ChartEntityEtx;
import com.xbongbong.pro.enums.ErrorCodeEnum;
import com.xbongbong.pro.enums.PackageStatusEnum;
import com.xbongbong.pro.enums.PackageTypeEnum;
import com.xbongbong.pro.enums.ProVersionEnum;
import com.xbongbong.pro.enums.ValueAddedTypeEnum;
import com.xbongbong.pro.enums.errorcodes.BiErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.ImportErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.enums.permission.enums.ProPermissionAliasEnum;
import com.xbongbong.pro.statistic.constant.DataSetConstant;
import com.xbongbong.pro.statistic.enums.ChartLimitTypeEnum;
import com.xbongbong.pro.statistic.model.DataSetModel;
import com.xbongbong.pro.statistic.pojo.DataSetBoardSaveInfoPojo;
import com.xbongbong.pro.statistic.pojo.DataSetPermissionPojo;
import com.xbongbong.pro.statistic.service.ChartLimitService;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.service.toolbox.help.PackageStatusHelp;
import com.xbongbong.sys.model.DepartmentModel;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author zcp
 * @version v1.0
 * @since v1.0
 */
@Component
public class DataSetHelp {
    @Resource
    private DataSetModel dataSetModel;
    @Resource
    private PackageHelp packageHelp;
    @Resource
    private PackageStatusHelp packageStatusHelp;
    @Resource
    private DepartmentModel departmentModel;
    @Resource
    private ChartLimitService chartLimitService;
    @Resource
    private PaasRedisHelper paasRedisHelper;


    /**
     * 获取数据集
     * @param id 数据集id
     * @param corpid
     * @return com.xbongbong.pro.domain.entity.DataSetEntity
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public DataSetEntity getDataSetEntity(Long id, String corpid) throws XbbException {
        DataSetEntity entity = dataSetModel.getByKey(id, corpid);
        if (Objects.isNull(entity) || Objects.equals(entity.getDel(), DelEnum.DELETE.getDel())) {
            throw new XbbException(ImportErrorCodeEnum.API_ERROR_235012, ImportErrorCodeEnum.API_ERROR_235012.getMsg(), XbbRefTypeEnum.DATA_SET.getName());
        }
        return entity;
    }
    /**
     * 获取数据集，并增加基础校验
     * @param id 数据集id
     * @param corpid
     * @param loginUser 登录人员
     * @return com.xbongbong.pro.domain.entity.DataSetEntity
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public DataSetEntity getDataSetEntityByCheck(Long id, String corpid, UserVO loginUser) throws XbbException {
        //权限、套餐
        hasPermission(loginUser);
        DataSetEntity entity = dataSetModel.getByKey(id, corpid);
        if (Objects.isNull(entity) || Objects.equals(entity.getDel(), DelEnum.DELETE.getDel())) {
            throw new XbbException(ImportErrorCodeEnum.API_ERROR_235012, ImportErrorCodeEnum.API_ERROR_235012.getMsg(), XbbRefTypeEnum.DATA_SET.getName());
        }
        // 获取部门
        Map<Long, List<Long>> subDepIdMap = departmentModel.getSubDepIdMap(corpid, false);
        Set<Long> depIdIn = CustomHelp.getSubDepIds(loginUser.getDepSet(), subDepIdMap);
        if (filterPermission4DataSet(DataSetPermissionEnum.EDITABLE_RULE, entity, loginUser, depIdIn)) {
            throw new XbbException(ErrorCodeEnum.API_ERROR_263015);
        }
        return entity;
    }
    /**
     * 校验登录人是否没有数据集编辑者权限或查看者权限 true代表没有权限
     * @param dataSetPermissionEnum 通过该参数确定是校验编辑者权限，还是校验查看者权限
     * @param entity 数据集实体
     * @param loginUser 登录人
     * @return boolean true 代表无权限，需要过滤；false 代表有权限
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public boolean filterPermission4DataSet(DataSetPermissionEnum dataSetPermissionEnum, DataSetEntity entity, UserVO loginUser, Set<Long> depIdIn){
        String userId = loginUser.getUserId();
        if (loginUser.isAdmin() || Objects.equals(entity.getCreatorId(), userId)) {
            // 超管或创建人权限不过虑 - 恒拥有编辑和查看权限
            return false;
        }
        JSONObject permission = entity.getPermission();
        if (Objects.isNull(permission) || permission.size() == 0) {
            //未设置则无权限
            return true;
        }
        DataSetPermissionPojo dataSetPermissionPojo = JsonHelperUtil.parseObject(JSONObject.toJSONString(permission), DataSetPermissionPojo.class);
        VisibleRulePoJo rulePojo;
        switch (dataSetPermissionEnum) {
            case EDITABLE_RULE:
                rulePojo = dataSetPermissionPojo.getEditableRule();
                break;
            case VISIBLE_SCOPE_RULE:
                rulePojo = dataSetPermissionPojo.getVisibleScopeRule();
                break;
            default:
                //不传入dataSetPermissionEnum，则强制代表无权限
                return true;
        }
        //未设置则强制代表无权限
        if (Objects.isNull(rulePojo)) {
            return true;
        }
        List<String> userIdList = rulePojo.getUser();
        if (CollectionsUtil.isNotEmpty(userIdList) && userIdList.contains(userId)) {
            //成员内有登录员工，则有权限
            return false;
        }
        List<VisibleRuleVO> dep = rulePojo.getDep();
        if (CollectionsUtil.isNotEmpty(dep)) {
            //设置的可编辑/可见部门是登录员工所在部门的父级部门
            for (VisibleRuleVO visibleRuleVO : dep) {
                if (depIdIn.contains(Long.parseLong(visibleRuleVO.getId().toString()))) {
                    return false;
                }
            }
        }
        List<Integer> role = rulePojo.getRole();
        Set<RoleSimpleVO> roleSet = loginUser.getRoleSet();
        //判断role集合中是否存在roleVOSet中的roleID
        Optional<RoleSimpleVO> roleOptional = roleSet.stream().filter(roleVO -> role.contains(roleVO.getId())).findFirst();
        if (roleOptional.isPresent()) {
            return false;
        }
        return true;
    }
    /**
     * 校验数据集权限、套餐、数据集数量（目前只有旗舰版数量是2个，其他都为0个 - 允许通过脚本更改数量）
     * @param corpid
     * @param loginUser 登录人员
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public void checkLimit(String corpid, UserVO loginUser) throws XbbException {
        //无权限、数量超限直接抛出
        checkLimit(corpid, loginUser, true);
    }
    /**
     * 校验数据集权限、套餐、数据集数量（目前只有旗舰版数量是2个，其他都为0个 - 允许通过脚本更改数量）
     * @param corpid
     * @param loginUser
     * @param throwFlag 无权限、数量超限时是否抛出标记，true抛出，false不抛出返回
     * @return boolean true 有权限或数量未超限，false 无权限或数量超限（只有throwFlag=true时才会返回false）
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public boolean checkLimit(String corpid, UserVO loginUser, boolean throwFlag) throws XbbException {
        //先校验权限、套餐是否为旗舰版
        boolean flag = hasPermission(loginUser, throwFlag);
        if (!flag) {
            //无权限直接返回，无需后续校验
            return flag;
        }
        //再校验数量
        return checkNum(corpid, throwFlag);
    }

    /**
     * 校验套餐数量
     * @param corpid
     * @param throwFlag 数量超限时是否抛出标记，true抛出，false不抛出返回
     * @return boolean true 数量未超限，false 数量超限
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public boolean checkNum(String corpid, boolean throwFlag) throws XbbException {
        if (ProBaseConfig.getProjectName().startsWith(ProVersionEnum.LOCAL.getAlias())) {
            //本地不校验数量
            return true;
        }
//        TODO 此处当给某家公司单独配置chartLimit后，会强制不走套餐判断，即下面的UpdatePackageRedisHelp.getValueAdded无法校验，后续如果要做成单独付费，此处要处理
        //如果某个公司单独放开数量，则走设置的限制数量
        boolean flag;
        if(chartLimitService.getChartLimtFlag(corpid, ChartLimitTypeEnum.DATA_SET_NUM_LIMIT.getAlias(), ChartLimitTypeEnum.DATA_SET_NUM_LIMIT)){
            int limit = 0;
            limit = chartLimitService.chartLimitNum(corpid, ChartLimitTypeEnum.DATA_SET_NUM_LIMIT.getCode(), limit);
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("del", 0);
            Integer count = dataSetModel.getEntitysCount(param);
            flag = count >= limit;
        } else {
            //数量
            Integer status = packageStatusHelp.getPackageStatus(corpid, ValueAddedTypeEnum.DATA_SET, BasicConstant.ONE);
            //焕勇：之前的设计是套餐是有个缓冲的范围（1/10的冗余），超过范围才被限制住。也就是如果放过WARNING，会出现用户可以在原有限制数量上面多建1个，因此此处把warning去掉
            flag = !Objects.equals(status, PackageStatusEnum.NORMAL.getCode());
        }
        if (flag) {
            //数量超限，则抛出或返回false
            if (throwFlag) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100074, I18nStringConstant.DATA_SET_NUM_LIMIT);
            }
            return false;
        }
        return true;
    }

    /**
     * 封装数据集列表内的操作按钮
     * @param editFlag true 无编辑权限
     * @param numOverFlag true 数量超限，无复制按钮
     * @return java.util.List<com.xbongbong.paas.pojo.AttrValuePojo>
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List<DsAttrValuePojo> formatDataSetOperate(boolean editFlag, boolean numOverFlag) {
        //操作
        List<DsAttrValuePojo> operate = new ArrayList<>();
        if (editFlag) {
            return operate;
        }
        List<DsAttrValuePojo> moreOperate = new ArrayList<>();
        for (DataSetOperateEnum cache : DataSetOperateEnum.getMoreOperate()) {
            if (numOverFlag && Objects.equals(DataSetOperateEnum.COPY.getAttr(), cache.getAttr())) {
                //套餐数量超限，则无复制按钮
                continue;
            }
            moreOperate.add(new DsAttrValuePojo(cache.getAttr(), cache.getValue(), cache.getIcon()));
        }
        for (DataSetOperateEnum cache : DataSetOperateEnum.getParentOperate()) {
            DsAttrValuePojo attrValuePojo = new DsAttrValuePojo(cache.getAttr(), cache.getValue(), cache.getIcon());
            if (Objects.equals(cache, DataSetOperateEnum.MORE)) {
                attrValuePojo.setChild(moreOperate);
            }
            operate.add(attrValuePojo);
        }
        return operate;
    }

    /**
     * 初始化数据集实体，其中permission也为初始化值 -- 未设置status、board
     * @param loginUser 登录人
     * @param name 数据集名称
     * @param memo 数据集备注
     * @return com.xbongbong.pro.domain.entity.DataSetEntity
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public DataSetEntity initDataSetEntity(UserVO loginUser, String name, String memo) {
        String corpid = loginUser.getCorpid();
        String userId = loginUser.getUserId();
        DataSetEntity entity = new DataSetEntity();
        entity.setCorpid(corpid);
        entity.setName(name);
        entity.setMemo(memo);
        //permission初始化处理：创建人、超管默认置入
        DataSetPermissionPojo dataSetPermissionPojo = new DataSetPermissionPojo(loginUser);
        JSONObject permissions = JsonHelperUtil.parseObject(JSONObject.toJSONString(dataSetPermissionPojo));
        entity.setPermission(permissions);
        //此处登录员工即创建人
        entity.setCreatorId(userId);
        long now = DateTimeUtil.getInt();
        entity.setDel(DelEnum.NORMAL.getDel());
        entity.setAddTime(now);
        entity.setUpdateTime(now);
        entity.setEditTime(now);
        return entity;
    }

    /**
     * 校验画板配置
     * @param board 画板配置，dto已经校验过board是否为空
     * @return com.alibaba.fastjson.JSONObject
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public JSONObject checkBoard(Map<String, JSONObject> board) throws XbbException {
        DataSetBoardSaveInfoPojo dataSetBoardSaveInfoPojo = new DataSetBoardSaveInfoPojo();
        //节点id - 该节点流出数量(即存在于其他节点input内的次数)
        Map<String, Integer> nidFlowCountMap = Maps.newHashMapWithExpectedSize(board.size());
        /*
          封装格式化节点信息 - 把JSONObject转为对应pojo，方便校验 - 节点校验在下方
          board已判空，key为节点id此处用不到(value内也有节点id)，因此只用values来遍历
         */
        for (JSONObject value : board.values()) {
            //先获取节点类型，再基于节点类型进行转换value
            String type = value.getString(BasicConstant.TYPE);
            DataSetNodeTypeEnum dataSetNodeTypeEnum = DataSetNodeTypeEnum.getByAlias(type);
            if (Objects.isNull(dataSetNodeTypeEnum)) {
                continue;
            }
            List<String> input = null;
            switch (dataSetNodeTypeEnum) {
                case INPUT:
                    DataSetNodeInputPojo inputPojo = JsonHelperUtil.parseObject(JSONObject.toJSONString(value), DataSetNodeInputPojo.class);
                    input = inputPojo.getInput();
                    if (Objects.nonNull(input) && input.size() > 0) {
                        throw new XbbException(ErrorCodeEnum.API_ERROR_263030, ErrorCodeEnum.API_ERROR_263030.getMsg(), inputPojo.getName());
                    }
                    dataSetBoardSaveInfoPojo.addInputNodeList(inputPojo);
                    break;
                case UNION:
                    DataSetNodeUnionPojo unionPojo = JsonHelperUtil.parseObject(JSONObject.toJSONString(value), DataSetNodeUnionPojo.class);
                    input = unionPojo.getInput();
                    dataSetBoardSaveInfoPojo.addUnionNodeList(unionPojo);
                    break;
                case JOIN:
                    DataSetNodeJoinPojo joinPojo = JsonHelperUtil.parseObject(JSONObject.toJSONString(value), DataSetNodeJoinPojo.class);
                    input = joinPojo.getInput();
                    dataSetBoardSaveInfoPojo.addJoinNodeList(joinPojo);
                    break;
                case GROUP:
                    DataSetNodeGroupPojo groupPojo = JsonHelperUtil.parseObject(JSONObject.toJSONString(value), DataSetNodeGroupPojo.class);
                    input = groupPojo.getInput();
                    dataSetBoardSaveInfoPojo.addGroupNodeList(groupPojo);
                    break;
                case OUTPUT:
                    DataSetNodeOutputPojo outputPojo = JsonHelperUtil.parseObject(JSONObject.toJSONString(value), DataSetNodeOutputPojo.class);
                    input = outputPojo.getInput();
                    dataSetBoardSaveInfoPojo.addOutputNodeList(outputPojo);
                    break;
                default:
                    break;
            }
            if (Objects.nonNull(input)) {
                //对该节点input内的所有节点来说，相当于该节点是他们的流出，所以要把遍历 input，并对其内节点的流出数量加1
                for (String nid : input) {
                    int flowCount = nidFlowCountMap.getOrDefault(nid, BasicConstant.ZERO);
                    flowCount ++;
                    nidFlowCountMap.put(nid, flowCount);
                }
            }
        }
        //校验节点
        checkNode(dataSetBoardSaveInfoPojo, nidFlowCountMap);
        //board校验合格后，要转为的JSONObject对象
        return JsonHelperUtil.parseObject(JSONObject.toJSONString(board));
    }

    /**
     * 校验是否有数据集权限
     * @param loginUser
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void hasPermission(UserVO loginUser) throws XbbException {
        //无权限直接抛出
        hasPermission(loginUser, true);
    }
    /**
     * 校验是否有数据集权限
     * @param loginUser 登录员工
     * @param throwFlag 无权限时是否抛出标记，true抛出，false不抛出返回
     * @return boolean true 有权限，false 无权限（只有throwFlag=true时才会返回false）
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private boolean hasPermission(UserVO loginUser, boolean throwFlag) throws XbbException {
        // 旗舰版才有
        Integer feeType = packageHelp.getFeeType(loginUser.getCorpid());
        if (!Objects.equals(feeType, PackageTypeEnum.ULTIMATE.getType())) {
            if (throwFlag) {
                throw new XbbException(ErrorCodeEnum.API_ERROR_263014);
            } else {
                return false;
            }
        }
        if (loginUser.isAdminOrBoss()) {
            return true;
        }
        Set<String> permissions = loginUser.getPermSet();
        ProPermissionAliasEnum proPermissionAliasEnum = ProPermissionAliasEnum.DATA_SET;
        String editPermissionName = proPermissionAliasEnum.getMemo();
        //数据集权限
        boolean permissionFlag = permissions.contains(proPermissionAliasEnum.getAlias());
        if (permissionFlag) {
            return true;
        }
        if (throwFlag) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100050, String.format(SystemErrorCodeEnum.API_ERROR_100050.getMsg(), editPermissionName));
        }
        return false;
    }

    /**
     * 校验节点
     *
     * @param dataSetBoardSaveInfoPojo 节点信息pojo
     * @param nidFlowCountMap 节点id - 该节点流出数量(即存在于其他节点input内的次数)
     * @throws XbbException
     * @author zcp
     * @version v1.0
     * @since v1.0
     */
    private void checkNode(DataSetBoardSaveInfoPojo dataSetBoardSaveInfoPojo, Map<String, Integer> nidFlowCountMap) throws XbbException {
        //封装完毕节点后，统一校验，以确保校验顺序：先校验数量，再校验输入格式。提高交互体验，防止出现用户辛苦修好格式，结果数量超限，需要再删除
        int totalOperateCount = 0;
        for (DataSetNodeTypeEnum nodeTypeEnum : DataSetNodeTypeEnum.values()) {
            switch (nodeTypeEnum) {
                case INPUT:
                    //数据源节点校验
                    checkNode4Input(dataSetBoardSaveInfoPojo, nidFlowCountMap, nodeTypeEnum);
                    break;
                case UNION:
                    int nodeCount = checkNode4Union(dataSetBoardSaveInfoPojo, nidFlowCountMap, nodeTypeEnum);
                    totalOperateCount += nodeCount;
                    break;
                case JOIN:
                    nodeCount = checkNode4Join(dataSetBoardSaveInfoPojo, nidFlowCountMap, nodeTypeEnum);
                    totalOperateCount += nodeCount;
                    break;
                case GROUP:
                    nodeCount = checkNode4Group(dataSetBoardSaveInfoPojo, nidFlowCountMap, nodeTypeEnum);
                    totalOperateCount += nodeCount;
                    break;
                case OUTPUT:
                    List<DataSetNodeOutputPojo> outputNodeList = dataSetBoardSaveInfoPojo.getOutputNodeList();
                    //校验该类型总节点数量
                    checkCount4Node(nodeTypeEnum, outputNodeList);
                    //校验节点
                    if (Objects.isNull(outputNodeList)) {
                        continue;
                    }
                    for (DataSetNodeOutputPojo node : outputNodeList) {
                        //校验该节点的节点名称，流入、流出数量
                        checkNodeFlow(nidFlowCountMap, nodeTypeEnum, node);
                    }
                    break;
                default:
                    break;
            }
        }
        if (totalOperateCount < DataSetConstant.MIN_OPERATE_NODE) {
            throw new XbbException(ErrorCodeEnum.API_ERROR_263037, ErrorCodeEnum.API_ERROR_263037.getMsg(), DataSetConstant.MIN_OPERATE_NODE);
        }
    }

    /**
     * 数据源节点校验
     * @param dataSetBoardSaveInfoPojo 节点信息pojo
     * @param nidFlowCountMap 节点id - 该节点流出数量(即存在于其他节点input内的次数)
     * @param nodeTypeEnum 节点类型
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void checkNode4Input(DataSetBoardSaveInfoPojo dataSetBoardSaveInfoPojo, Map<String, Integer> nidFlowCountMap, DataSetNodeTypeEnum nodeTypeEnum) throws XbbException {
        List<DataSetNodeInputPojo> nodeList = dataSetBoardSaveInfoPojo.getInputNodeList();
        //校验该类型总节点数量
        checkCount4Node(nodeTypeEnum, nodeList);
        if (Objects.isNull(nodeList)) {
            return;
        }
        String typeName = nodeTypeEnum.getName();
        //校验节点
        for (DataSetNodeInputPojo node : nodeList) {
            //校验该节点的节点名称，流入、流出数量
            checkNodeFlow(nidFlowCountMap, nodeTypeEnum, node);
            //校验数据源节点特色的属性
            List<String> fieldChecked = node.getFieldChecked();
            if (CollectionsUtil.isEmpty(fieldChecked)) {
                throw new XbbException(ErrorCodeEnum.API_ERROR_263038, ErrorCodeEnum.API_ERROR_263038.getMsg(), typeName, node.getName());
            }
            int count = fieldChecked.size();
            if (count > DataSetConstant.MAX_FIELD_COUNT) {
                throw new XbbException(ErrorCodeEnum.API_ERROR_263039, ErrorCodeEnum.API_ERROR_263039.getMsg(), typeName, node.getName(), count, DataSetConstant.MAX_FIELD_COUNT);
            }
        }
    }

    /**
     * 合并节点校验
     * @param dataSetBoardSaveInfoPojo 节点信息pojo
     * @param nidFlowCountMap 节点id - 该节点流出数量(即存在于其他节点input内的次数)
     * @param nodeTypeEnum 节点类型
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private int checkNode4Union(DataSetBoardSaveInfoPojo dataSetBoardSaveInfoPojo, Map<String, Integer> nidFlowCountMap, DataSetNodeTypeEnum nodeTypeEnum) throws XbbException {
        List<DataSetNodeUnionPojo> unionNodeList = dataSetBoardSaveInfoPojo.getUnionNodeList();
        //无合并节点，则跳过后续节点校验
        if (CollectionsUtil.isEmpty(unionNodeList)) {
            return BasicConstant.ZERO;
        }
        //校验该类型总节点数量
        int nodeCount = checkCount4Node(nodeTypeEnum, unionNodeList);
        String typeName = nodeTypeEnum.getName();
        for (DataSetNodeUnionPojo node : unionNodeList) {
            //校验该节点的节点名称，流入、流出数量
            checkNodeFlow(nidFlowCountMap, nodeTypeEnum, node);
            //校验各节点特色的属性
            List<DataSetNodeFieldPojo> field = node.getField();
            if (CollectionsUtil.isEmpty(field)) {
                throw new XbbException(ErrorCodeEnum.API_ERROR_263040, ErrorCodeEnum.API_ERROR_263040.getMsg(), typeName, node.getName());
            }
            int count = field.size();
            if (count > DataSetConstant.MAX_FIELD_COUNT) {
                throw new XbbException(ErrorCodeEnum.API_ERROR_263039, ErrorCodeEnum.API_ERROR_263039.getMsg(), typeName, node.getName(), count, DataSetConstant.MAX_FIELD_COUNT);
            }
            for (DataSetNodeFieldPojo fieldPojo : field) {
                String attrName = fieldPojo.getAttrName();
                if (StringUtil.isEmpty(attrName, true)) {
                    throw new XbbException(ErrorCodeEnum.API_ERROR_263041, ErrorCodeEnum.API_ERROR_263041.getMsg(), typeName, node.getName());
                }
                boolean flag = Objects.isNull(fieldPojo.getFormId()) || StringUtil.isEmpty(fieldPojo.getOriginalAttr()) || StringUtil.isEmpty(fieldPojo.getAttr()) || Objects.isNull(fieldPojo.getFieldType());
                if (flag) {
                    throw new XbbException(ErrorCodeEnum.API_ERROR_263042, ErrorCodeEnum.API_ERROR_263042.getMsg(), typeName, node.getName(), attrName);
                }
                Map<String, BiExplainPojo> srcB = fieldPojo.getSrcB();
                if (Objects.isNull(srcB) || srcB.size() <= BasicConstant.ZERO) {
                    throw new XbbException(ErrorCodeEnum.API_ERROR_263042, ErrorCodeEnum.API_ERROR_263042.getMsg(), typeName, node.getName(), attrName);
                }
            }
        }
        return nodeCount;
    }

    /**
     * 连接节点校验
     * @param dataSetBoardSaveInfoPojo 节点信息pojo
     * @param nidFlowCountMap 节点id - 该节点流出数量(即存在于其他节点input内的次数)
     * @param nodeTypeEnum 节点类型
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private int checkNode4Join(DataSetBoardSaveInfoPojo dataSetBoardSaveInfoPojo, Map<String, Integer> nidFlowCountMap, DataSetNodeTypeEnum nodeTypeEnum) throws XbbException {
        List<DataSetNodeJoinPojo> joinNodeList = dataSetBoardSaveInfoPojo.getJoinNodeList();
        //无连接节点，则跳过后续节点校验
        if (CollectionsUtil.isEmpty(joinNodeList)) {
            return BasicConstant.ZERO;
        }
        //校验该类型总节点数量
        int nodeCount = checkCount4Node(nodeTypeEnum, joinNodeList);
        String typeName = nodeTypeEnum.getName();
        List<Integer> abnormalStatusList = DataSetFieldStatusEnum.abnormalStatusList();
        for (DataSetNodeJoinPojo node : joinNodeList) {
            //校验该节点的节点名称，流入、流出数量
            checkNodeFlow(nidFlowCountMap, nodeTypeEnum, node);
            String nodeName = node.getName();
            DataSetJoinEnum dataSetJoinEnum = DataSetJoinEnum.getByAlias(node.getJoin());
            if (Objects.isNull(dataSetJoinEnum)) {
                throw new XbbException(ErrorCodeEnum.API_ERROR_263046, ErrorCodeEnum.API_ERROR_263046.getMsg(), typeName, nodeName);
            }
            //校验连接节点特色的属性
            List<Map<String, String>> relation = node.getRelation();
            //连接节点的field及内部的srcB是后端映射生成
            List<DataSetNodeFieldPojo> field = node.getField();
            if (CollectionsUtil.isEmpty(relation) || CollectionsUtil.isEmpty(field)) {
                throw new XbbException(ErrorCodeEnum.API_ERROR_263043, ErrorCodeEnum.API_ERROR_263043.getMsg(), typeName, nodeName);
            }
            if (relation.size() > DataSetConstant.MAX_LINK_FIELD_COUNT) {
                throw new XbbException(ErrorCodeEnum.API_ERROR_263045, ErrorCodeEnum.API_ERROR_263045.getMsg(), typeName, nodeName, DataSetConstant.MAX_LINK_FIELD_COUNT);
            }
            int count = 0;
            //连接节点的field(formId、srcB等)是后端生成，前端不会改，所以此处不校验
            for (DataSetNodeFieldPojo fieldPojo : field) {
                if (abnormalStatusList.contains(fieldPojo.getStatus())) {
                    continue;
                }
                count++;
            }
            if (count > DataSetConstant.MAX_FIELD_COUNT) {
                throw new XbbException(ErrorCodeEnum.API_ERROR_263039, ErrorCodeEnum.API_ERROR_263039.getMsg(), typeName, nodeName, count, DataSetConstant.MAX_FIELD_COUNT);
            }
            if (count <= BasicConstant.ZERO) {
                throw new XbbException(ErrorCodeEnum.API_ERROR_263044, ErrorCodeEnum.API_ERROR_263044.getMsg(), typeName, nodeName);
            }
        }
        return nodeCount;
    }

    /**
     * 分组汇总节点校验
     * @param dataSetBoardSaveInfoPojo 节点信息pojo
     * @param nidFlowCountMap 节点id - 该节点流出数量(即存在于其他节点input内的次数)
     * @param nodeTypeEnum 节点类型
     * @return int
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private int checkNode4Group(DataSetBoardSaveInfoPojo dataSetBoardSaveInfoPojo, Map<String, Integer> nidFlowCountMap, DataSetNodeTypeEnum nodeTypeEnum) throws XbbException {
        List<DataSetNodeGroupPojo> groupNodeList = dataSetBoardSaveInfoPojo.getGroupNodeList();
        //无分组汇总节点，则跳过后续节点校验
        if (CollectionsUtil.isEmpty(groupNodeList)) {
            return BasicConstant.ZERO;
        }
        //校验该类型总节点数量
        int nodeCount = checkCount4Node(nodeTypeEnum, groupNodeList);
        String typeName = nodeTypeEnum.getName();
        List<Integer> timeIdList = FieldTypeEnum.esaliasTypeList().getTimeIdList();
        for (DataSetNodeGroupPojo node : groupNodeList) {
            //校验该节点的节点名称，流入、流出数量
            checkNodeFlow(nidFlowCountMap, nodeTypeEnum, node);
            //校验分组汇总节点特色的属性
            List<DataSetNodeFieldPojo> group = node.getGroup();
            List<DataSetNodeFieldPojo> sum = node.getSum();
            String nodeName = node.getName();
            if (CollectionsUtil.isEmpty(group) || CollectionsUtil.isEmpty(sum)) {
                throw new XbbException(ErrorCodeEnum.API_ERROR_263047, ErrorCodeEnum.API_ERROR_263047.getMsg(), typeName, nodeName);
            }
            for (DataSetNodeFieldPojo fieldPojo : group) {
                //日期类型要设置分组方式
                boolean timeFlag = timeIdList.contains(fieldPojo.getFieldType()) && StringUtil.isEmpty(fieldPojo.getDateType());
                if (timeFlag) {
                    throw new XbbException(ErrorCodeEnum.API_ERROR_263048, ErrorCodeEnum.API_ERROR_263048.getMsg(), typeName, nodeName, fieldPojo.getAttrName());
                }
            }
            for (DataSetNodeFieldPojo fieldPojo : sum) {
                if (StringUtil.isEmpty(fieldPojo.getAggType())) {
                    throw new XbbException(ErrorCodeEnum.API_ERROR_263049, ErrorCodeEnum.API_ERROR_263049.getMsg(), typeName, nodeName, fieldPojo.getAttrName());
                }
            }
        }
        return nodeCount;
    }

    /**
     * 校验某种节点类型的总节点数量
     * @param nodeTypeEnum 节点类型
     * @param nodeList 该类型节点list
     * @return int
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private int checkCount4Node(DataSetNodeTypeEnum nodeTypeEnum, List<? extends DataSetNodePojo> nodeList) throws XbbException {
        int nodeCount = 0;
        if (Objects.nonNull(nodeList)) {
            nodeCount = nodeList.size();
        }
        int minCount = nodeTypeEnum.getMinCount();
        int maxCount = nodeTypeEnum.getMaxCount();
        if (nodeCount < minCount) {
            throw new XbbException(ErrorCodeEnum.API_ERROR_263032, ErrorCodeEnum.API_ERROR_263032.getMsg(), minCount, nodeTypeEnum.getName());
        }
        if (nodeCount > maxCount) {
            throw new XbbException(ErrorCodeEnum.API_ERROR_263033, ErrorCodeEnum.API_ERROR_263033.getMsg(), maxCount, nodeTypeEnum.getName());
        }
        return nodeCount;
    }

    /**
     * 校验某个节点的节点名称，流入、流出数量
     * @param nidFlowCountMap 节点id - 该节点流出数量(即存在于其他节点input内的次数)
     * @param nodeTypeEnum 节点类型
     * @param node 节点
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void checkNodeFlow(Map<String, Integer> nidFlowCountMap, DataSetNodeTypeEnum nodeTypeEnum, DataSetNodePojo node) throws XbbException {
        //校验节点名称
        String name = node.getName();
        //节点类型名称
        String typeName = nodeTypeEnum.getName();
        if (StringUtil.isEmpty(name, true)) {
            throw new XbbException(ErrorCodeEnum.API_ERROR_263031, ErrorCodeEnum.API_ERROR_263031.getMsg(), typeName);
        }
        //校验节点的流入、流出数量
        List<String> input = node.getInput();
        int inputCount = 0;
        if (Objects.nonNull(input)) {
            inputCount = input.size();
        }
        int flowInMin = nodeTypeEnum.getFlowInMin();
        int flowInMax = nodeTypeEnum.getFlowInMax();
        int flowOutCount = nodeTypeEnum.getFlowOutCount();
        if (inputCount < flowInMin) {
            throw new XbbException(ErrorCodeEnum.API_ERROR_263034, ErrorCodeEnum.API_ERROR_263034.getMsg(), typeName, name, flowInMin);
        }
        if (inputCount > flowInMax) {
            throw new XbbException(ErrorCodeEnum.API_ERROR_263035, ErrorCodeEnum.API_ERROR_263035.getMsg(), typeName, name, flowInMax);
        }
        int nodeFlowOutCount = nidFlowCountMap.getOrDefault(node.getId(), BasicConstant.ZERO);
        if (!Objects.equals(nodeFlowOutCount, flowOutCount)) {
            throw new XbbException(ErrorCodeEnum.API_ERROR_263036, ErrorCodeEnum.API_ERROR_263036.getMsg(), typeName, name, flowOutCount);
        }
    }

    /**
     * 标记图表是否需要被重置
     *
     * @param chartEntityEtx 图表实体
     * @param dataSetCompareTime 图表上次与数据集对比的时间
     * @author zyy
     * @date 2022/10/11
     * @version v1.0
     */
    public boolean compareDataSetTime(ChartEntityEtx chartEntityEtx,Long dataSetCompareTime,DataSetEntity dataSetEntity) throws XbbException {
        //判断是否需要重置
        boolean flag = resetChartByDataSet(chartEntityEtx.getCorpid(),chartEntityEtx.getDriverSources(),chartEntityEtx.getSingle(),dataSetCompareTime,dataSetEntity);
        if(flag){
            chartEntityEtx.setDataSetEditFlag(Boolean.TRUE);
        }
        return flag;
    }

    /**
     * 判断数据源为数据集的图表是否需要重置
     *
     * @param corpid 公司主键
     * @param driverSources 主表数据
     * @param single 0-多表 1-单表  2-数据集
     * @param dataSetCompareTime  图表上次与数据集对比的时间
     * @return boolean
     * @author zyy
     * @date 2022/10/12
     * @version v1.0
     */
    public boolean resetChartByDataSet(String corpid, JSONObject driverSources, Integer single, Long dataSetCompareTime,DataSetEntity dataSetEntity) throws XbbException {
        //非数据集、或无图表与数据集对比时间直接返回false，不需要判断
        if(!ChartSourceEnum.isDataSet(single) || dataSetCompareTime == null){
            return false;
        }
        if(Objects.isNull(dataSetEntity)){
            Long dataSetId = driverSources.getLong(XbbConstant.DATA_SET_ID);
            dataSetEntity = getDataSetEntity(dataSetId, corpid);
        }
        Long editTime = dataSetEntity.getUpdateTime();
        if(Objects.nonNull(dataSetEntity.getEditTime())){
            editTime = dataSetEntity.getEditTime();
        }
        return editTime.compareTo(dataSetCompareTime) > 0;
    }

    public DataSetEntity getDsNodeField(String corpid, Long dsId,List<FieldAttrEntity> fieldList) throws XbbException {
        DataSetEntity entity = dataSetModel.getByKey(dsId, corpid);
        JSONObject board = entity.getBoard();
        //找到输出节点
        JSONObject jsonObjectOutput = null;
        for (Map.Entry<String, Object> stringObjectEntry : board.entrySet()) {
            JSONObject jsonObject = JSON.parseObject(stringObjectEntry.getValue().toString());
            if(Objects.equals(jsonObject.getString("type"), DataSetNodeTypeEnum.OUTPUT.getAlias())){
                jsonObjectOutput = jsonObject;
                break;
            }
        }
        if(jsonObjectOutput ==  null){
            //TODO   找不到输出节点报错
//            throw
        }
        DataSetNodeOutputPojo  dataSetNodeOutputPojo  = jsonObjectOutput.toJavaObject(DataSetNodeOutputPojo.class);
        //找到输出节点的上一个节点
        JSONObject preNodeJson = board.getJSONObject(dataSetNodeOutputPojo.getInput().get(0));
        List<DataSetNodeFieldPojo> field = null;
        if(Objects.equals(preNodeJson.getString("type"), DataSetNodeTypeEnum.UNION.getAlias())){
            DataSetNodeUnionPojo dataSetNodePojo = JSONObject.parseObject(preNodeJson.toJSONString(), DataSetNodeUnionPojo.class);
            field = dataSetNodePojo.getField();
            List<DataSetNodeFieldPojo> hideField = dataSetNodePojo.getHideField();
            if(CollectionsUtil.isNotEmpty(hideField) && CollectionsUtil.isNotEmpty(field)){
                for (DataSetNodeFieldPojo dataSetNodeFieldPojo : hideField) {
                    //合并的时候把关联字段待后面选择加进来
                    if(!field.contains(dataSetNodeFieldPojo) && !Objects.equals(dataSetNodeFieldPojo.getFieldType(),FieldTypeEnum.ID.getType()) && !Objects.equals(dataSetNodeFieldPojo.getFieldType(),FieldTypeEnum.FORM_ID.getType())){
                        field.add(dataSetNodeFieldPojo);
                    }
                }
            }
        }else if (Objects.equals(preNodeJson.getString("type"), DataSetNodeTypeEnum.JOIN.getAlias())){
            DataSetNodeJoinPojo dataSetNodePojo = JSONObject.parseObject(preNodeJson.toJSONString(), DataSetNodeJoinPojo.class);
            field = dataSetNodePojo.getField();
        }

        if(CollectionsUtil.isEmpty(field)){
            throw new XbbException(BiErrorCodeEnum.API_ERROR_241041);
        }
        try {
            BeanUtil.copyPropertiesList(field,fieldList,FieldAttrEntity.class);
        } catch (Exception e) {
            //TODO  等错误枚举归纳好
//            LOG.error("",e);
//            throw new XbbException()
        }

        //核心不在这个返回值，主要是设置fieldList的数据
        return entity;
    }

    /**
     * 删除公司下数据集使用数量的缓存key
     *
     * @param corpid 公司唯一标识
     * @author zyy
     * @date 2022/11/10
     * @version v1.0
     */
    public void delPackageNumRedisKey(String corpid){
        String key = corpid + XbbConstant.SEPARATOR + ValueAddedTypeEnum.DATA_SET.getType();
        if(paasRedisHelper.hasKey(RedisPrefixConstant.VALUE_ADDED,key)){
            paasRedisHelper.removeValue(RedisPrefixConstant.VALUE_ADDED,key);
        }
    }

    /**
     * 根据数据集id，批量获取数据集图表
     *
     * @param corpid
     * @param dsChartList
     * @return java.util.List<com.xbongbong.pro.domain.entity.DataSetEntity>
     * @author zyy
     * @date 2023/3/14
     * @version v1.0
     */
    public List<DataSetEntity> dataSetEntityList(String corpid,List<ChartEntityEtx> dsChartList) {
        if(CollectionsUtil.isEmpty(dsChartList)){
            return new ArrayList<>();
        }
        List<Long> dsIdList = dsChartList.stream().map(chart -> chart.getDriverSources().getLong(XbbConstant.DATA_SET_ID)).collect(Collectors.toList());
        Map<String,Object> paramMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        paramMap.put(StringConstant.IDIN,dsIdList);
        paramMap.put(StringConstant.CORPID,corpid);
        return dataSetModel.findEntitys(paramMap);
    }
}
